home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 10 / AACD 10.iso / AACD / Games / WarpQuake / Src / net_comx.c < prev    next >
C/C++ Source or Header  |  2000-05-22  |  31KB  |  1,286 lines

  1. /*
  2. Copyright (C) 1996-1997 Id Software, Inc.
  3.  
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  12.  
  13. See the GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. */
  20. // net_comx.c
  21.  
  22. #include <dos.h>
  23. #include <dpmi.h>
  24.  
  25. #define NUM_COM_PORTS    2
  26.  
  27. #define ERR_TTY_LINE_STATUS        -1
  28. #define ERR_TTY_MODEM_STATUS    -2
  29. #define ERR_TTY_NODATA            -3
  30.  
  31. #define QUEUESIZE    8192
  32. #define QUEUEMASK    (QUEUESIZE - 1)
  33.  
  34. typedef struct
  35. {
  36.     volatile int  head;
  37.     volatile int  tail;
  38.     volatile byte data[QUEUESIZE];
  39. } queue;
  40.  
  41. #define FULL(q)            (q.head == ((q.tail-1) & QUEUEMASK))
  42. #define EMPTY(q)        (q.tail == q.head)
  43. #define ENQUEUE(q,b)    (q.data[q.head] = b, q.head = (q.head + 1) & QUEUEMASK)
  44. #define DEQUEUE(q,b)    (b = q.data[q.tail], q.tail = (q.tail + 1) & QUEUEMASK)
  45.  
  46. extern cvar_t    config_com_port;
  47. extern cvar_t    config_com_irq;
  48. extern cvar_t    config_com_baud;
  49. extern cvar_t    config_com_modem;
  50. extern cvar_t    config_modem_dialtype;
  51. extern cvar_t    config_modem_clear;
  52. extern cvar_t    config_modem_init;
  53. extern cvar_t    config_modem_hangup;
  54.  
  55. extern int m_return_state;
  56. extern int m_state;
  57. extern qboolean m_return_onerror;
  58. extern char m_return_reason[32];
  59.  
  60. // 8250, 16550 definitions
  61. #define TRANSMIT_HOLDING_REGISTER            0x00
  62. #define RECEIVE_BUFFER_REGISTER              0x00
  63. #define INTERRUPT_ENABLE_REGISTER            0x01
  64. #define   IER_RX_DATA_READY                  0x01
  65. #define   IER_TX_HOLDING_REGISTER_EMPTY      0x02
  66. #define   IER_LINE_STATUS                    0x04
  67. #define   IER_MODEM_STATUS                   0x08
  68. #define INTERRUPT_ID_REGISTER                0x02
  69. #define   IIR_MODEM_STATUS_INTERRUPT         0x00
  70. #define   IIR_TX_HOLDING_REGISTER_INTERRUPT  0x02
  71. #define   IIR_RX_DATA_READY_INTERRUPT        0x04
  72. #define   IIR_LINE_STATUS_INTERRUPT          0x06
  73. #define   IIR_FIFO_TIMEOUT                   0x0c
  74. #define   IIR_FIFO_ENABLED                   0xc0
  75. #define FIFO_CONTROL_REGISTER                0x02
  76. #define   FCR_FIFO_ENABLE                    0x01
  77. #define   FCR_RCVR_FIFO_RESET                0x02
  78. #define   FCR_XMIT_FIFO_RESET                0x04
  79. #define   FCR_TRIGGER_01                     0x00
  80. #define   FCR_TRIGGER_04                     0x40
  81. #define   FCR_TRIGGER_08                     0x80
  82. #define   FCR_TRIGGER_16                     0xc0
  83. #define LINE_CONTROL_REGISTER                0x03
  84. #define   LCR_DATA_BITS_5                    0x00
  85. #define   LCR_DATA_BITS_6                    0x01
  86. #define   LCR_DATA_BITS_7                    0x02
  87. #define   LCR_DATA_BITS_8                    0x03
  88. #define   LCR_STOP_BITS_1                    0x00
  89. #define   LCR_STOP_BITS_2                    0x04
  90. #define   LCR_PARITY_NONE                    0x00
  91. #define   LCR_PARITY_ODD                     0x08
  92. #define   LCR_PARITY_EVEN                    0x18
  93. #define   LCR_PARITY_MARK                    0x28
  94. #define   LCR_PARITY_SPACE                   0x38
  95. #define   LCR_SET_BREAK                      0x40
  96. #define   LCR_DLAB                           0x80
  97. #define MODEM_CONTROL_REGISTER               0x04
  98. #define   MCR_DTR                            0x01
  99. #define   MCR_RTS                            0x02
  100. #define   MCR_OUT1                           0x04
  101. #define   MCR_OUT2                           0x08
  102. #define   MCR_LOOPBACK                       0x10
  103. #define LINE_STATUS_REGISTER                 0x05
  104. #define   LSR_DATA_READY                     0x01
  105. #define   LSR_OVERRUN_ERROR                  0x02
  106. #define   LSR_PARITY_ERROR                   0x04
  107. #define   LSR_FRAMING_ERROR                  0x08
  108. #define   LSR_BREAK_DETECT                   0x10
  109. #define   LSR_TRANSMITTER_BUFFER_EMPTY       0x20
  110. #define   LSR_TRANSMITTER_EMPTY              0x40
  111. #define   LSR_FIFO_DIRTY                     0x80
  112. #define MODEM_STATUS_REGISTER                0x06
  113. #define   MSR_DELTA_CTS                      0x01
  114. #define   MSR_DELTA_DSR                      0x02
  115. #define   MSR_DELTA_RI                       0x04
  116. #define   MSR_DELTA_CD                       0x08
  117. #define   MSR_CTS                            0x10
  118. #define   MSR_DSR                            0x20
  119. #define   MSR_RI                             0x40
  120. #define   MSR_CD                             0x80
  121. #define DIVISOR_LATCH_LOW                    0x00
  122. #define DIVISOR_LATCH_HIGH                   0x01
  123.  
  124. #define MODEM_STATUS_MASK    (MSR_CTS | MSR_DSR | MSR_CD)
  125.  
  126. #define UART_AUTO    0
  127. #define UART_8250    1
  128. #define UART_16550    2
  129.  
  130. static int ISA_uarts[]    = {0x3f8,0x2f8,0x3e8,0x2e8};
  131. static int ISA_IRQs[]    = {4,3,4,3};
  132.  
  133. typedef struct ComPort_s
  134. {
  135.     struct ComPort_s        *next;
  136.     _go32_dpmi_seginfo        protectedModeInfo;
  137.     _go32_dpmi_seginfo        protectedModeSaveInfo;
  138.     int                        uart;
  139.     volatile byte            modemStatus;
  140.     byte                    modemStatusIgnore;
  141.     byte                    lineStatus;
  142.     byte                    bufferUsed;
  143.     qboolean                enabled;
  144.     volatile qboolean        statusUpdated;
  145.     qboolean                useModem;
  146.     qboolean                modemInitialized;
  147.     qboolean                modemRang;
  148.     qboolean                modemConnected;
  149.     queue                    inputQueue;
  150.     queue                    outputQueue;
  151.     char                    clear[16];
  152.     char                    startup[32];
  153.     char                    shutdown[16];
  154.     char                    buffer[128];
  155.     PollProcedure            poll;
  156.     double                    timestamp;
  157.     byte                    uartType;
  158.     byte                    irq;
  159.     byte                    baudBits;
  160.     byte                    lineControl;
  161.     byte                    portNumber;
  162.     char                    dialType;
  163.     char                    name[4];
  164. } ComPort;
  165.  
  166. ComPort *portList = NULL;
  167. ComPort *handleToPort [NUM_COM_PORTS];
  168.  
  169. static int Modem_Command(ComPort *p, char *commandString);
  170. static char *Modem_Response(ComPort *p);
  171. static void Modem_Hangup(ComPort *p);
  172.  
  173. int TTY_Init(void);
  174. void TTY_Shutdown(void);
  175. int TTY_Open(int serialPortNumber);
  176. void TTY_Close(int handle);
  177. int TTY_ReadByte(int handle);
  178. int TTY_WriteByte(int handle, byte data);
  179. void TTY_Flush(int handle);
  180. int TTY_Connect(int handle, char *host);
  181. void TTY_Disconnect(int handle);
  182. qboolean TTY_CheckForConnection(int handle);
  183. qboolean TTY_IsEnabled(int serialPortNumber);
  184. qboolean TTY_IsModem(int serialPortNumber);
  185. qboolean TTY_OutputQueueIsEmpty(int handle);
  186.  
  187. static void ISR_8250 (ComPort *p)
  188. {
  189.     byte    source = 0;
  190.     byte    b;
  191.  
  192.     disable();
  193.  
  194.     while((source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07) != 1)
  195.     {
  196.         switch (source)
  197.         {
  198.             case IIR_RX_DATA_READY_INTERRUPT:
  199.                 b = inportb (p->uart + RECEIVE_BUFFER_REGISTER);
  200.                 if (! FULL(p->inputQueue))
  201.                 {
  202.                     ENQUEUE (p->inputQueue, b);
  203.                 }
  204.                 else
  205.                 {
  206.                     p->lineStatus |= LSR_OVERRUN_ERROR;
  207.                     p->statusUpdated = true;
  208.                 }
  209.                 break;
  210.  
  211.             case IIR_TX_HOLDING_REGISTER_INTERRUPT:
  212.                 if (! EMPTY(p->outputQueue))
  213.                 {
  214.                     DEQUEUE (p->outputQueue, b);
  215.                     outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b);
  216.                 }
  217.                 break;
  218.  
  219.             case IIR_MODEM_STATUS_INTERRUPT:
  220.                 p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore;
  221.                 p->statusUpdated = true;
  222.                 break;
  223.  
  224.             case IIR_LINE_STATUS_INTERRUPT:
  225.                 p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER);
  226.                 p->statusUpdated = true;
  227.                 break;
  228.         }
  229.         source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07;
  230.     }
  231.     outportb (0x20, 0x20);
  232. }
  233.  
  234. static void COM1_ISR_8250 (void)
  235. {
  236.     ISR_8250 (handleToPort[0]);
  237. }
  238.  
  239. static void COM2_ISR_8250 (void)
  240. {
  241.     ISR_8250 (handleToPort[1]);
  242. }
  243.  
  244.  
  245.  
  246. static void ISR_16550 (ComPort *p)
  247. {
  248.     int        count;
  249.     byte    source;
  250.     byte    b;
  251.  
  252.     disable();
  253.     while((source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07) != 1)
  254.     {
  255.         switch (source)
  256.         {
  257.             case IIR_RX_DATA_READY_INTERRUPT:
  258.                 do
  259.                 {
  260.                     b = inportb (p->uart + RECEIVE_BUFFER_REGISTER);
  261.                     if (!FULL(p->inputQueue))
  262.                     {
  263.                         ENQUEUE (p->inputQueue, b);
  264.                     }
  265.                     else
  266.                     {
  267.                         p->lineStatus |= LSR_OVERRUN_ERROR;
  268.                         p->statusUpdated = true;
  269.                     }
  270.                 } while (inportb (p->uart + LINE_STATUS_REGISTER) & LSR_DATA_READY);
  271.                 break;
  272.  
  273.             case IIR_TX_HOLDING_REGISTER_INTERRUPT:
  274.                 count = 16;
  275.                 while ((! EMPTY(p->outputQueue)) && count--)
  276.                 {
  277.                     DEQUEUE (p->outputQueue, b);
  278.                     outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b);
  279.                 }
  280.                 break;
  281.  
  282.             case IIR_MODEM_STATUS_INTERRUPT:
  283.                 p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore;
  284.                 p->statusUpdated = true;
  285.                 break;
  286.  
  287.             case IIR_LINE_STATUS_INTERRUPT:
  288.                 p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER);
  289.                 p->statusUpdated = true;
  290.                 break;
  291.         }
  292.         source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07;
  293.     }
  294.  
  295.     // check for lost IIR_TX_HOLDING_REGISTER_INTERRUPT on 16550a!
  296.     if (inportb (p->uart + LINE_STATUS_REGISTER ) & LSR_TRANSMITTER_EMPTY)
  297.     {
  298.         count = 16;
  299.         while ((! EMPTY(p->outputQueue)) && count--)
  300.         {
  301.             DEQUEUE (p->outputQueue, b);
  302.             outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b);
  303.         }
  304.     }
  305.  
  306.     outportb (0x20, 0x20);
  307. }
  308.  
  309. static void COM1_ISR_16550 (void)
  310. {
  311.     ISR_16550 (handleToPort[0]);
  312. }
  313.  
  314. static void COM2_ISR_16550 (void)
  315. {
  316.     ISR_16550 (handleToPort[1]);
  317. }
  318.  
  319.  
  320. void TTY_GetComPortConfig (int portNumber, int *port, int *irq, int *baud, qboolean *useModem)
  321. {
  322.     ComPort    *p;
  323.  
  324.     p = handleToPort[portNumber];
  325.     *port = p->uart;
  326.     *irq = p->irq;
  327.     *baud = 115200 / p->baudBits;
  328.     *useModem = p->useModem;
  329. }
  330.  
  331. void TTY_SetComPortConfig (int portNumber, int port, int irq, int baud, qboolean useModem)
  332. {
  333.     ComPort    *p;
  334.     float    temp;
  335.  
  336.     if (useModem)
  337.     {
  338.         if (baud == 14400)
  339.             baud = 19200;
  340.         if (baud == 28800)
  341.             baud = 38400;
  342.     }
  343.  
  344.     p = handleToPort[portNumber];
  345.     p->uart = port;
  346.     p->irq = irq;
  347.     p->baudBits = 115200 / baud;
  348.     p->useModem = useModem;
  349.  
  350.     if (useModem)
  351.         temp = 1.0;
  352.     else
  353.         temp = 0.0;
  354.  
  355.     Cvar_SetValue ("_config_com_port", (float)port);
  356.     Cvar_SetValue ("_config_com_irq", (float)irq);
  357.     Cvar_SetValue ("_config_com_baud", (float)baud);
  358.     Cvar_SetValue ("_config_com_modem", temp);
  359. }
  360.  
  361. void TTY_GetModemConfig (int portNumber, char *dialType, char *clear, char *init, char *hangup)
  362. {
  363.     ComPort    *p;
  364.  
  365.     p = handleToPort[portNumber];
  366.     *dialType = p->dialType;
  367.     Q_strcpy(clear, p->clear);
  368.     Q_strcpy(init, p->startup);
  369.     Q_strcpy(hangup, p->shutdown);
  370. }
  371.  
  372. void TTY_SetModemConfig (int portNumber, char *dialType, char *clear, char *init, char *hangup)
  373. {
  374.     ComPort    *p;
  375.  
  376.     p = handleToPort[portNumber];
  377.     p->dialType = dialType[0];
  378.     Q_strcpy(p->clear, clear);
  379.     Q_strcpy(p->startup, init);
  380.     Q_strcpy(p->shutdown, hangup);
  381.  
  382.     p->modemInitialized = false;
  383.  
  384.     Cvar_Set ("_config_modem_dialtype", dialType);
  385.     Cvar_Set ("_config_modem_clear", clear);
  386.     Cvar_Set ("_config_modem_init", init);
  387.     Cvar_Set ("_config_modem_hangup", hangup);
  388. }
  389.  
  390.  
  391. static void ResetComPortConfig (ComPort *p)
  392. {
  393.     p->useModem = false;
  394.     p->uartType = UART_AUTO;
  395.     p->uart = ISA_uarts[p->portNumber];
  396.     p->irq = ISA_IRQs[p->portNumber];
  397.     p->modemStatusIgnore = MSR_CD | MSR_CTS | MSR_DSR;
  398.     p->baudBits = 115200 / 57600;
  399.     p->lineControl = LCR_DATA_BITS_8 | LCR_STOP_BITS_1 | LCR_PARITY_NONE;
  400.     Q_strcpy(p->clear, "ATZ");
  401.     Q_strcpy(p->startup, "");
  402.     Q_strcpy(p->shutdown, "AT H");
  403.     p->modemRang = false;
  404.     p->modemConnected = false;
  405.     p->statusUpdated = false;
  406.     p->outputQueue.head = p->outputQueue.tail = 0;
  407.     p->inputQueue.head = p->inputQueue.tail = 0;
  408. }
  409.  
  410.  
  411. static void ComPort_Enable(ComPort *p)
  412. {
  413.     void    (*isr)(void);
  414.     int        n;
  415.     byte    b;
  416.  
  417.     if (p->enabled)
  418.     {
  419.         Con_Printf("Already enabled\n");
  420.         return;
  421.     }
  422.  
  423.     // disable all UART interrupts
  424.     outportb (p->uart + INTERRUPT_ENABLE_REGISTER, 0);
  425.  
  426.     // clear out any buffered uncoming data
  427.     while((inportb (p->uart + LINE_STATUS_REGISTER)) & LSR_DATA_READY)
  428.         inportb (p->uart + RECEIVE_BUFFER_REGISTER);
  429.  
  430.     // get the current line and modem status
  431.     p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore;
  432.     p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER);
  433.  
  434.     // clear any UART interrupts
  435.     do
  436.     {
  437.         n = inportb (p->uart + INTERRUPT_ID_REGISTER) & 7;
  438.         if (n == IIR_RX_DATA_READY_INTERRUPT)
  439.             inportb (p->uart + RECEIVE_BUFFER_REGISTER);
  440.     } while (!(n & 1));
  441.  
  442.     if (p->uartType == UART_AUTO)
  443.     {
  444.         outportb (p->uart + FIFO_CONTROL_REGISTER, FCR_FIFO_ENABLE);
  445.         b = inportb (p->uart + INTERRUPT_ID_REGISTER);
  446.         if ((b & IIR_FIFO_ENABLED) == IIR_FIFO_ENABLED)
  447.             p->uartType = UART_16550;
  448.         else
  449.             p->uartType = UART_8250;
  450.     }
  451.  
  452.     // save the old interrupt handler
  453.     _go32_dpmi_get_protected_mode_interrupt_vector(p->irq + 8, &p->protectedModeSaveInfo);
  454.  
  455.     if (p->uartType == UART_8250)
  456.     {
  457.         outportb (p->uart + FIFO_CONTROL_REGISTER, 0);
  458.         if (p == handleToPort[0])
  459.             isr = COM1_ISR_8250;
  460.         else
  461.             isr = COM2_ISR_8250;
  462.     }
  463.     else
  464.     {
  465.         outportb (p->uart + FIFO_CONTROL_REGISTER, FCR_FIFO_ENABLE | FCR_RCVR_FIFO_RESET | FCR_XMIT_FIFO_RESET | FCR_TRIGGER_08);
  466.         if (p == handleToPort[0])
  467.             isr = COM1_ISR_16550;
  468.         else
  469.             isr = COM2_ISR_16550;
  470.     }
  471.  
  472.     p->protectedModeInfo.pm_offset = (int)isr;
  473.  
  474.     n = _go32_dpmi_allocate_iret_wrapper(&p->protectedModeInfo);
  475.     if (n)
  476.     {
  477.         Con_Printf("serial: protected mode callback allocation failed\n");
  478.         return;
  479.     }
  480.  
  481.     // disable interrupts at the processor
  482.     disable();
  483.  
  484.     // install our interrupt handlers now
  485.     _go32_dpmi_set_protected_mode_interrupt_vector(p->irq + 8, &p->protectedModeInfo);
  486.  
  487.     // enable our interrupt at the PIC
  488.     outportb (0x21, inportb (0x21) & ~(1<<p->irq));
  489.  
  490.     // enable interrupts at the processor
  491.     enable();
  492.  
  493.     // enable interrupts at the PIC
  494.     outportb (0x20, 0xc2);
  495.  
  496.     // set baud rate & line control
  497.     outportb (p->uart + LINE_CONTROL_REGISTER, LCR_DLAB | p->lineControl);
  498.     outportb (p->uart, p->baudBits);
  499.     outportb (p->uart + 1, 0);
  500.     outportb (p->uart + LINE_CONTROL_REGISTER, p->lineControl);
  501.  
  502.     // set modem control register & enable uart interrupt generation
  503.     outportb(p->uart + MODEM_CONTROL_REGISTER, MCR_OUT2 | MCR_RTS | MCR_DTR);
  504.  
  505.     // enable the individual interrupts at the uart
  506.     outportb (p->uart + INTERRUPT_ENABLE_REGISTER, IER_RX_DATA_READY | IER_TX_HOLDING_REGISTER_EMPTY | IER_LINE_STATUS | IER_MODEM_STATUS);
  507.  
  508.     p->enabled = true;
  509. }
  510.  
  511.  
  512. static void ComPort_Disable(ComPort *p)
  513. {
  514.     if (!p->enabled)
  515.     {
  516.         Con_Printf("Already disabled\n");
  517.         return;
  518.     }
  519.  
  520.     // disable interrupts at the uart
  521.     outportb (p->uart + INTERRUPT_ENABLE_REGISTER, 0);
  522.  
  523.     // disable our interrupt at the PIC
  524.     outportb (0x21, inportb (0x21) | (1<<p->irq));
  525.  
  526.     // disable interrupts at the processor
  527.     disable();
  528.  
  529.     // restore the old interrupt handler
  530.     _go32_dpmi_set_protected_mode_interrupt_vector(p->irq + 8, &p->protectedModeSaveInfo);
  531.     _go32_dpmi_free_iret_wrapper(&p->protectedModeInfo);
  532.  
  533.     // enable interrupts at the processor
  534.     enable();
  535.  
  536.     p->enabled = false;
  537. }
  538.  
  539.  
  540. static int CheckStatus (ComPort *p)
  541. {
  542.     int        ret = 0;
  543.  
  544.     if (p->statusUpdated)
  545.     {
  546.         p->statusUpdated = false;
  547.  
  548.         if (p->lineStatus & (LSR_OVERRUN_ERROR | LSR_PARITY_ERROR | LSR_FRAMING_ERROR | LSR_BREAK_DETECT))
  549.         {
  550.             if (p->lineStatus & LSR_OVERRUN_ERROR)
  551.                 Con_DPrintf ("Serial overrun error\n");
  552.             if (p->lineStatus & LSR_PARITY_ERROR)
  553.                 Con_DPrintf ("Serial parity error\n");
  554.             if (p->lineStatus & LSR_FRAMING_ERROR)
  555.                 Con_DPrintf ("Serial framing error\n");
  556.             if (p->lineStatus & LSR_BREAK_DETECT)
  557.                 Con_DPrintf ("Serial break detect\n");
  558.             ret = ERR_TTY_LINE_STATUS;
  559.         }
  560.  
  561.         if ((p->modemStatus & MODEM_STATUS_MASK) != MODEM_STATUS_MASK)
  562.         {
  563.             if (!(p->modemStatus & MSR_CTS))
  564.                 Con_Printf ("Serial lost CTS\n");
  565.             if (!(p->modemStatus & MSR_DSR))
  566.                 Con_Printf ("Serial lost DSR\n");
  567.             if (!(p->modemStatus & MSR_CD))
  568.                 Con_Printf ("Serial lost Carrier\n");
  569.             ret = ERR_TTY_MODEM_STATUS;
  570.         }
  571.     }
  572.  
  573.     return ret;
  574. }
  575.  
  576.  
  577. static void Modem_Init(ComPort *p)
  578. {
  579.     double    start;
  580.     char    *response;
  581.  
  582.     Con_Printf ("Initializing modem...\n");
  583.  
  584.     // write 0 to MCR, wait 1/2 sec, then write the real value back again
  585.     // I got this from the guys at head-to-head who say it's necessary.
  586.     outportb(p->uart + MODEM_CONTROL_REGISTER, 0);
  587.     start = Sys_FloatTime();
  588.     while ((Sys_FloatTime() - start) < 0.5)
  589.         ;
  590.     outportb(p->uart + MODEM_CONTROL_REGISTER, MCR_OUT2 | MCR_RTS | MCR_DTR);
  591.     start = Sys_FloatTime();
  592.     while ((Sys_FloatTime() - start) < 0.25)
  593.         ;
  594.  
  595.     if (*p->clear)
  596.     {
  597.         Modem_Command (p, p->clear);
  598.         start = Sys_FloatTime();
  599.         while(1)
  600.         {
  601.             if ((Sys_FloatTime() - start) > 3.0)
  602.             {
  603.                 Con_Printf("No response - clear failed\n");
  604.                 p->enabled = false;
  605.                 goto failed;
  606.             }
  607.             response = Modem_Response(p);
  608.             if (!response)
  609.                 continue;
  610.             if (Q_strncmp(response, "OK", 2) == 0)
  611.                 break;
  612.             if (Q_strncmp(response, "ERROR", 5) == 0)
  613.             {
  614.                 p->enabled = false;
  615.                 goto failed;
  616.             }
  617.         }
  618.     }
  619.  
  620.     if (*p->startup)
  621.     {
  622.         Modem_Command (p, p->startup);
  623.         start = Sys_FloatTime();
  624.         while(1)
  625.         {
  626.             if ((Sys_FloatTime() - start) > 3.0)
  627.             {
  628.                 Con_Printf("No response - init failed\n");
  629.                 p->enabled = false;
  630.                 goto failed;
  631.             }
  632.             response = Modem_Response(p);
  633.             if (!response)
  634.                 continue;
  635.             if (Q_strncmp(response, "OK", 2) == 0)
  636.                 break;
  637.             if (Q_strncmp(response, "ERROR", 5) == 0)
  638.             {
  639.                 p->enabled = false;
  640.                 goto failed;
  641.             }
  642.         }
  643.     }
  644.  
  645.     p->modemInitialized = true;
  646.     return;
  647.  
  648. failed:
  649.     if (m_return_onerror)
  650.     {
  651.         key_dest = key_menu;
  652.         m_state = m_return_state;
  653.         m_return_onerror = false;
  654.         Q_strcpy(m_return_reason, "Initialization Failed");
  655.     }
  656.     return;
  657. }
  658.  
  659.  
  660. void TTY_Enable(int handle)
  661. {
  662.     ComPort    *p;
  663.  
  664.     p = handleToPort [handle];
  665.     if (p->enabled)
  666.         return;
  667.  
  668.     ComPort_Enable(p);
  669.  
  670.     if (p->useModem && !p->modemInitialized)
  671.         Modem_Init (p);
  672. }
  673.  
  674.  
  675. int TTY_Open(int serialPortNumber)
  676. {
  677.     return serialPortNumber;
  678. }
  679.  
  680.  
  681. void TTY_Close(int handle)
  682. {
  683.     ComPort    *p;
  684.     double        startTime;
  685.  
  686.     p = handleToPort [handle];
  687.  
  688.     startTime = Sys_FloatTime();
  689.     while ((Sys_FloatTime() - startTime) < 1.0)
  690.         if (EMPTY(p->outputQueue))
  691.             break;
  692.  
  693.     if (p->useModem)
  694.     {
  695.         if (p->modemConnected)
  696.             Modem_Hangup(p);
  697.     }
  698. }
  699.  
  700.  
  701. int TTY_ReadByte(int handle)
  702. {
  703.     int        ret;
  704.     ComPort    *p;
  705.  
  706.     p = handleToPort [handle];
  707.  
  708.     if ((ret = CheckStatus (p)) != 0)
  709.         return ret;
  710.     
  711.     if (EMPTY (p->inputQueue))
  712.         return ERR_TTY_NODATA;
  713.  
  714.     DEQUEUE (p->inputQueue, ret);
  715.     return (ret & 0xff);
  716. }
  717.  
  718.  
  719. int TTY_WriteByte(int handle, byte data)
  720. {
  721.     ComPort    *p;
  722.  
  723.     p = handleToPort [handle];
  724.     if (FULL(p->outputQueue))
  725.         return -1;
  726.  
  727.     ENQUEUE (p->outputQueue, data);
  728.     return 0;
  729. }
  730.  
  731.  
  732. void TTY_Flush(int handle)
  733. {
  734.     byte b;
  735.     ComPort    *p;
  736.  
  737.     p = handleToPort [handle];
  738.  
  739.     if (inportb (p->uart + LINE_STATUS_REGISTER ) & LSR_TRANSMITTER_EMPTY)
  740.     {
  741.         DEQUEUE (p->outputQueue, b);
  742.         outportb(p->uart, b);
  743.     }
  744. }
  745.  
  746.  
  747. int TTY_Connect(int handle, char *host)
  748. {
  749.     double    start;
  750.     ComPort    *p;
  751.     char    *response = NULL;
  752.     keydest_t    save_key_dest;
  753.     byte    dialstring[64];
  754.     byte    b;
  755.  
  756.     p = handleToPort[handle];
  757.  
  758.     if ((p->modemStatus & MODEM_STATUS_MASK) != MODEM_STATUS_MASK)
  759.     {
  760.         Con_Printf ("Serial: line not ready (");
  761.         if ((p->modemStatus & MSR_CTS) == 0)
  762.             Con_Printf(" CTS");
  763.         if ((p->modemStatus & MSR_DSR) == 0)
  764.             Con_Printf(" DSR");
  765.         if ((p->modemStatus & MSR_CD) == 0)
  766.             Con_Printf(" CD");
  767.         Con_Printf(" )");
  768.         return -1;
  769.     }
  770.  
  771.     // discard any scraps in the input buffer
  772.     while (! EMPTY (p->inputQueue))
  773.         DEQUEUE (p->inputQueue, b);
  774.  
  775.     CheckStatus (p);
  776.  
  777.     if (p->useModem)
  778.     {
  779.         save_key_dest = key_dest;
  780.         key_dest = key_console;
  781.         key_count = -2;
  782.  
  783.         Con_Printf ("Dialing...\n");
  784.         sprintf(dialstring, "AT D%c %s\r", p->dialType, host);
  785.         Modem_Command (p, dialstring);
  786.         start = Sys_FloatTime();
  787.         while(1)
  788.         {
  789.             if ((Sys_FloatTime() - start) > 60.0)
  790.             {
  791.                 Con_Printf("Dialing failure!\n");
  792.                 break;
  793.             }
  794.  
  795.             Sys_SendKeyEvents ();
  796.             if (key_count == 0)
  797.             {
  798.                 if (key_lastpress != K_ESCAPE)
  799.                 {
  800.                     key_count = -2;
  801.                     continue;
  802.                 }
  803.                 Con_Printf("Aborting...\n");
  804.                 while ((Sys_FloatTime() - start) < 5.0)
  805.                     ;
  806.                 disable();
  807.                 p->outputQueue.head = p->outputQueue.tail = 0;
  808.                 p->inputQueue.head = p->inputQueue.tail = 0;
  809.                 outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) & ~MCR_DTR);
  810.                 enable();
  811.                 start = Sys_FloatTime();
  812.                 while ((Sys_FloatTime() - start) < 0.75)
  813.                     ;
  814.                 outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) | MCR_DTR);
  815.                 response = "Aborted";
  816.                 break;
  817.             }
  818.  
  819.             response = Modem_Response(p);
  820.             if (!response)
  821.                 continue;
  822.             if (Q_strncmp(response, "CONNECT", 7) == 0)
  823.             {
  824.                 disable();
  825.                 p->modemRang = true;
  826.                 p->modemConnected = true;
  827.                 p->outputQueue.head = p->outputQueue.tail = 0;
  828.                 p->inputQueue.head = p->inputQueue.tail = 0;
  829.                 enable();
  830.                 key_dest = save_key_dest;
  831.                 key_count = 0;
  832.                 m_return_onerror = false;
  833.                 return 0;
  834.             }
  835.             if (Q_strncmp(response, "NO CARRIER", 10) == 0)
  836.                 break;
  837.             if (Q_strncmp(response, "NO DIALTONE", 11) == 0)
  838.                 break;
  839.             if (Q_strncmp(response, "NO DIAL TONE", 12) == 0)
  840.                 break;
  841.             if (Q_strncmp(response, "NO ANSWER", 9) == 0)
  842.                 break;
  843.             if (Q_strncmp(response, "BUSY", 4) == 0)
  844.                 break;
  845.             if (Q_strncmp(response, "ERROR", 5) == 0)
  846.                 break;
  847.         }
  848.         key_dest = save_key_dest;
  849.         key_count = 0;
  850.         if (m_return_onerror)
  851.         {
  852.             key_dest = key_menu;
  853.             m_state = m_return_state;
  854.             m_return_onerror = false;
  855.             Q_strncpy(m_return_reason, response, 31);
  856.         }
  857.         return -1;
  858.     }
  859.     m_return_onerror = false;
  860.     return 0;
  861. }
  862.  
  863.  
  864. void TTY_Disconnect(int handle)
  865. {
  866.     ComPort *p;
  867.  
  868.     p = handleToPort[handle];
  869.  
  870.     if (p->useModem && p->modemConnected)
  871.         Modem_Hangup(p);
  872. }
  873.  
  874.  
  875. qboolean TTY_CheckForConnection(int handle)
  876. {
  877.     ComPort    *p;
  878.  
  879.     p = handleToPort[handle];
  880.  
  881.     CheckStatus (p);
  882.  
  883.     if (p->useModem)
  884.     {
  885.         if (!p->modemRang)
  886.         {
  887.             if (!Modem_Response(p))
  888.                 return false;
  889.  
  890.             if (Q_strncmp(p->buffer, "RING", 4) == 0)
  891.             {
  892.                 Modem_Command (p, "ATA");
  893.                 p->modemRang = true;
  894.                 p->timestamp = net_time;
  895.             }
  896.             return false;
  897.         }
  898.         if (!p->modemConnected)
  899.         {
  900.             if ((net_time - p->timestamp) > 35.0)
  901.             {
  902.                 Con_Printf("Unable to establish modem connection\n");
  903.                 p->modemRang = false;
  904.                 return false;
  905.             }
  906.  
  907.             if (!Modem_Response(p))
  908.                 return false;
  909.  
  910.             if (Q_strncmp (p->buffer, "CONNECT", 7) != 0)
  911.                 return false;
  912.  
  913.             disable();
  914.             p->modemConnected = true;
  915.             p->outputQueue.head = p->outputQueue.tail = 0;
  916.             p->inputQueue.head = p->inputQueue.tail = 0;
  917.             enable();
  918.             Con_Printf("Modem Connect\n");
  919.             return true;
  920.         }
  921.         return true;
  922.     }
  923.  
  924.     // direct connect case
  925.     if (EMPTY (p->inputQueue))
  926.         return false;
  927.     return true;
  928. }
  929.  
  930.  
  931. qboolean TTY_IsEnabled(int serialPortNumber)
  932. {
  933.     return handleToPort[serialPortNumber]->enabled;
  934. }
  935.  
  936.  
  937. qboolean TTY_IsModem(int serialPortNumber)
  938. {
  939.     return handleToPort[serialPortNumber]->useModem;
  940. }
  941.  
  942.  
  943. qboolean TTY_OutputQueueIsEmpty(int handle)
  944. {
  945.     return EMPTY(handleToPort[handle]->outputQueue);
  946. }
  947.  
  948.  
  949. void Com_f (void)
  950. {
  951.     ComPort    *p;
  952.     int        portNumber;
  953.     int        i;
  954.     int        n;
  955.  
  956.     // first, determine which port they're messing with
  957.     portNumber = Q_atoi(Cmd_Argv (0) + 3) - 1;
  958.     if (portNumber > 1)
  959.         return;
  960.     p = handleToPort[portNumber];
  961.  
  962.     if (Cmd_Argc() == 1)
  963.     {
  964.         Con_Printf("Settings for COM%i\n", portNumber + 1);
  965.         Con_Printf("enabled:   %s\n", p->enabled ? "true" : "false");
  966.         Con_Printf("uart:      ");
  967.         if (p->uartType == UART_AUTO)
  968.             Con_Printf("auto\n");
  969.         else if (p->uartType == UART_8250)
  970.             Con_Printf("8250\n");
  971.         else
  972.             Con_Printf("16550\n");
  973.         Con_Printf("port:      %x\n", p->uart);
  974.         Con_Printf("irq:       %i\n", p->irq);
  975.         Con_Printf("baud:      %i\n", 115200 / p->baudBits);    
  976.         Con_Printf("CTS:       %s\n", (p->modemStatusIgnore & MSR_CTS) ? "ignored" : "honored");
  977.         Con_Printf("DSR:       %s\n", (p->modemStatusIgnore & MSR_DSR) ? "ignored" : "honored");
  978.         Con_Printf("CD:        %s\n", (p->modemStatusIgnore & MSR_CD) ? "ignored" : "honored");
  979.         if (p->useModem)
  980.         {
  981.             Con_Printf("type:      Modem\n");
  982.             Con_Printf("clear:     %s\n", p->clear);
  983.             Con_Printf("startup:   %s\n", p->startup);
  984.             Con_Printf("shutdown:  %s\n", p->shutdown);
  985.         }
  986.         else
  987.             Con_Printf("type:      Direct connect\n");
  988.  
  989.         return;
  990.     }
  991.  
  992.  
  993.     if (Cmd_CheckParm ("disable"))
  994.     {
  995.         if (p->enabled)
  996.             ComPort_Disable(p);
  997.         p->modemInitialized = false;
  998.         return;
  999.     }
  1000.  
  1001.     if (Cmd_CheckParm ("reset"))
  1002.     {
  1003.         ComPort_Disable(p);
  1004.         ResetComPortConfig (p);
  1005.         return;
  1006.     }
  1007.  
  1008.     if ((i = Cmd_CheckParm ("port")) != 0)
  1009.     {
  1010.         if (p->enabled)
  1011.             {
  1012.                 Con_Printf("COM port must be disabled to change port\n");
  1013.                 return;
  1014.             }
  1015.         p->uart = Q_atoi (Cmd_Argv (i+1));
  1016.     }
  1017.  
  1018.     if ((i = Cmd_CheckParm ("irq")) != 0)
  1019.     {
  1020.         if (p->enabled)
  1021.             {
  1022.                 Con_Printf("COM port must be disabled to change irq\n");
  1023.                 return;
  1024.             }
  1025.         p->irq = Q_atoi (Cmd_Argv (i+1));
  1026.     }
  1027.  
  1028.     if ((i = Cmd_CheckParm ("baud")) != 0)
  1029.     {
  1030.         if (p->enabled)
  1031.             {
  1032.                 Con_Printf("COM port must be disabled to change baud\n");
  1033.                 return;
  1034.             }
  1035.         n = Q_atoi (Cmd_Argv (i+1));
  1036.         if (n == 0)
  1037.             Con_Printf("Invalid baud rate specified\n");
  1038.         else
  1039.             p->baudBits = 115200 / n;
  1040.     }
  1041.  
  1042.     if (Cmd_CheckParm ("8250"))
  1043.     {
  1044.         if (p->enabled)
  1045.             {
  1046.                 Con_Printf("COM port must be disabled to change uart\n");
  1047.                 return;
  1048.             }
  1049.         p->uartType = UART_8250;
  1050.         }
  1051.     if (Cmd_CheckParm ("16550"))
  1052.     {
  1053.         if (p->enabled)
  1054.             {
  1055.                 Con_Printf("COM port must be disabled to change uart\n");
  1056.                 return;
  1057.             }
  1058.         p->uartType = UART_16550;
  1059.     }
  1060.     if (Cmd_CheckParm ("auto"))
  1061.     {
  1062.         if (p->enabled)
  1063.             {
  1064.                 Con_Printf("COM port must be disabled to change uart\n");
  1065.                 return;
  1066.             }
  1067.         p->uartType = UART_AUTO;
  1068.     }
  1069.  
  1070.     if (Cmd_CheckParm ("pulse"))
  1071.         p->dialType = 'P';
  1072.     if (Cmd_CheckParm ("tone"))
  1073.         p->dialType = 'T';
  1074.  
  1075.     if (Cmd_CheckParm ("direct"))
  1076.         p->useModem = false;
  1077.     if (Cmd_CheckParm ("modem"))
  1078.         p->useModem = true;
  1079.  
  1080.     if ((i = Cmd_CheckParm ("clear")) != 0)
  1081.     {
  1082.         Q_strncpy (p->clear, Cmd_Argv (i+1), 16);
  1083.     }
  1084.  
  1085.     if ((i = Cmd_CheckParm ("startup")) != 0)
  1086.     {
  1087.         Q_strncpy (p->startup, Cmd_Argv (i+1), 32);
  1088.         p->modemInitialized = false;
  1089.     }
  1090.  
  1091.     if ((i = Cmd_CheckParm ("shutdown")) != 0)
  1092.     {
  1093.         Q_strncpy (p->shutdown, Cmd_Argv (i+1), 16);
  1094.     }
  1095.  
  1096.     if (Cmd_CheckParm ("-cts"))
  1097.     {
  1098.         p->modemStatusIgnore |= MSR_CTS;
  1099.         p->modemStatus |= MSR_CTS;
  1100.     }
  1101.  
  1102.     if (Cmd_CheckParm ("+cts"))
  1103.     {
  1104.         p->modemStatusIgnore &= (~MSR_CTS);
  1105.         p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore;
  1106.     }
  1107.  
  1108.     if (Cmd_CheckParm ("-dsr"))
  1109.     {
  1110.         p->modemStatusIgnore |= MSR_DSR;
  1111.         p->modemStatus |= MSR_DSR;
  1112.     }
  1113.  
  1114.     if (Cmd_CheckParm ("+dsr"))
  1115.     {
  1116.         p->modemStatusIgnore &= (~MSR_DSR);
  1117.         p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore;
  1118.     }
  1119.  
  1120.     if (Cmd_CheckParm ("-cd"))
  1121.     {
  1122.         p->modemStatusIgnore |= MSR_CD;
  1123.         p->modemStatus |= MSR_CD;
  1124.     }
  1125.  
  1126.     if (Cmd_CheckParm ("+cd"))
  1127.     {
  1128.         p->modemStatusIgnore &= (~MSR_CD);
  1129.         p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore;
  1130.     }
  1131.  
  1132.     if (Cmd_CheckParm ("enable"))
  1133.     {
  1134.         if (!p->enabled)
  1135.             ComPort_Enable(p);
  1136.         if (p->useModem && !p->modemInitialized)
  1137.             Modem_Init (p);
  1138.     }
  1139. }
  1140.  
  1141.  
  1142. int TTY_Init(void)
  1143. {
  1144.     int        n;
  1145.     ComPort *p;
  1146.  
  1147.     for (n = 0; n < NUM_COM_PORTS; n++)
  1148.     {
  1149.         p = (ComPort *)Hunk_AllocName(sizeof(ComPort), "comport");
  1150.         if (p == NULL)
  1151.             Sys_Error("Hunk alloc failed for com port\n");
  1152.         p->next = portList;
  1153.         portList = p;
  1154.         handleToPort[n] = p;
  1155.         p->portNumber = n;
  1156.         p->dialType = 'T';
  1157.         sprintf(p->name, "com%u", n+1);
  1158.         Cmd_AddCommand (p->name, Com_f);
  1159.         ResetComPortConfig (p);
  1160.     }
  1161.  
  1162.     GetComPortConfig = TTY_GetComPortConfig;
  1163.     SetComPortConfig = TTY_SetComPortConfig;
  1164.     GetModemConfig = TTY_GetModemConfig;
  1165.     SetModemConfig = TTY_SetModemConfig;
  1166.  
  1167.     return 0;
  1168. }
  1169.  
  1170.  
  1171. void TTY_Shutdown(void)
  1172. {
  1173.     int        n;
  1174.     ComPort *p;
  1175.  
  1176.     for (n = 0; n < NUM_COM_PORTS; n++)
  1177.     {
  1178.         p = handleToPort[n];
  1179.         if (p->enabled)
  1180.         {
  1181.             while (p->modemConnected)
  1182.                 NET_Poll();
  1183.             ComPort_Disable (p);
  1184.         }
  1185.     }
  1186. }
  1187.  
  1188.  
  1189. static int Modem_Command(ComPort *p, char *commandString)
  1190. {
  1191.     byte    b;
  1192.  
  1193.     if (CheckStatus (p))
  1194.         return -1;
  1195.  
  1196.     disable();
  1197.     p->outputQueue.head = p->outputQueue.tail = 0;
  1198.     p->inputQueue.head = p->inputQueue.tail = 0;
  1199.     enable();
  1200.     p->bufferUsed = 0;
  1201.  
  1202.     while (*commandString)
  1203.         ENQUEUE (p->outputQueue, *commandString++);
  1204.     ENQUEUE (p->outputQueue, '\r');
  1205.  
  1206.     // get the transmit rolling
  1207.     DEQUEUE (p->outputQueue, b);
  1208.     outportb(p->uart, b);
  1209.  
  1210.     return 0;
  1211. }
  1212.  
  1213.  
  1214. static char *Modem_Response(ComPort *p)
  1215. {
  1216.     byte    b;
  1217.  
  1218.     if (CheckStatus (p))
  1219.         return NULL;
  1220.  
  1221.     while (! EMPTY(p->inputQueue))
  1222.     {
  1223.         DEQUEUE (p->inputQueue, b);
  1224.  
  1225.         if (p->bufferUsed == (sizeof(p->buffer) - 1))
  1226.             b = '\r';
  1227.  
  1228.         if (b == '\r' && p->bufferUsed)
  1229.         {
  1230.             p->buffer[p->bufferUsed] = 0;
  1231.             Con_Printf("%s\n", p->buffer);
  1232.             SCR_UpdateScreen ();
  1233.             p->bufferUsed = 0;
  1234.             return p->buffer;
  1235.         }
  1236.  
  1237.         if (b < ' ' || b > 'z')
  1238.             continue;
  1239.         p->buffer[p->bufferUsed] = b;
  1240.         p->bufferUsed++;
  1241.     }
  1242.  
  1243.     return NULL;
  1244. }
  1245.  
  1246.  
  1247. static void Modem_Hangup2(ComPort *p);
  1248. static void Modem_Hangup3(ComPort *p);
  1249. static void Modem_Hangup4(ComPort *p);
  1250.  
  1251. static void Modem_Hangup(ComPort *p)
  1252. {
  1253.     Con_Printf("Hanging up modem...\n");
  1254.     disable();
  1255.     p->modemRang = false;
  1256.     p->outputQueue.head = p->outputQueue.tail = 0;
  1257.     p->inputQueue.head = p->inputQueue.tail = 0;
  1258.     outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) & ~MCR_DTR);
  1259.     enable();
  1260.     p->poll.procedure = Modem_Hangup2;
  1261.     p->poll.arg = p;
  1262.     SchedulePollProcedure(&p->poll, 1.5);
  1263. }
  1264.  
  1265. static void Modem_Hangup2(ComPort *p)
  1266. {
  1267.     outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) | MCR_DTR);
  1268.     Modem_Command(p, "+++");
  1269.     p->poll.procedure = Modem_Hangup3;
  1270.     SchedulePollProcedure(&p->poll, 1.5);
  1271. }
  1272.  
  1273. static void Modem_Hangup3(ComPort *p)
  1274. {
  1275.     Modem_Command(p, p->shutdown);
  1276.     p->poll.procedure = Modem_Hangup4;
  1277.     SchedulePollProcedure(&p->poll, 1.5);
  1278. }
  1279.  
  1280. static void Modem_Hangup4(ComPort *p)
  1281. {
  1282.     Modem_Response(p);
  1283.     Con_Printf("Hangup complete\n");
  1284.     p->modemConnected = false;
  1285. }
  1286.